package com.hjq.demo.http.model

import android.app.Application
import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.net.ConnectivityManager
import cn.cucsi.myframe.R
import com.google.gson.JsonSyntaxException
import com.hjq.demo.http.exception.ResultException
import com.hjq.demo.http.exception.TokenException
import com.hjq.demo.http.model.HttpCacheManager.generateCacheKey
import com.hjq.demo.http.model.HttpCacheManager.isCacheInvalidate
import com.hjq.demo.http.model.HttpCacheManager.readHttpCache
import com.hjq.demo.http.model.HttpCacheManager.setHttpCacheTime
import com.hjq.demo.http.model.HttpCacheManager.writeHttpCache
import com.hjq.gson.factory.GsonFactory
import com.hjq.http.EasyLog
import com.hjq.http.config.IRequestHandler
import com.hjq.http.exception.CancelException
import com.hjq.http.exception.DataException
import com.hjq.http.exception.FileMd5Exception
import com.hjq.http.exception.HttpException
import com.hjq.http.exception.NetworkException
import com.hjq.http.exception.NullBodyException
import com.hjq.http.exception.ResponseException
import com.hjq.http.exception.ServerException
import com.hjq.http.exception.TimeoutException
import com.hjq.http.request.HttpRequest
import okhttp3.Headers
import okhttp3.Response
import okhttp3.ResponseBody
import java.io.IOException
import java.io.InputStream
import java.lang.reflect.GenericArrayType
import java.lang.reflect.Type
import java.net.SocketTimeoutException
import java.net.UnknownHostException

/**
 * author : Android 轮子哥
 * github : https://github.com/getActivity/EasyHttp
 * time   : 2019/05/19
 * desc   : 请求处理类
 */
class RequestHandler(private val mApplication: Application) : IRequestHandler {
    @Throws(Throwable::class)
    override fun requestSuccess(httpRequest: HttpRequest<*>, response: Response, type: Type): Any {
        if (Response::class.java == type) {
            return response
        }

        if (!response.isSuccessful) {
            throw ResponseException(
                String.format(
                    mApplication.getString(R.string.http_response_error),
                    response.code(), response.message()
                ), response
            )
        }

        if (Headers::class.java == type) {
            return response.headers()
        }

        val body = response.body()
            ?: throw NullBodyException(mApplication.getString(R.string.http_response_null_body))

        if (ResponseBody::class.java == type) {
            return body
        }

        // 如果是用数组接收，判断一下是不是用 byte[] 类型进行接收的
        if (type is GenericArrayType) {
            val genericComponentType = type.genericComponentType
            if (Byte::class.javaPrimitiveType == genericComponentType) {
                return body.bytes()
            }
        }

        if (InputStream::class.java == type) {
            return body.byteStream()
        }

        if (Bitmap::class.java == type) {
            return BitmapFactory.decodeStream(body.byteStream())
        }

        val text: String
        try {
            text = body.string()
        } catch (e: IOException) {
            // 返回结果读取异常
            throw DataException(mApplication.getString(R.string.http_data_explain_error), e)
        }

        // 打印这个 Json 或者文本
        EasyLog.printJson(httpRequest, text)

        if (String::class.java == type) {
            return text
        }

        val result: Any

        try {
            result = GsonFactory.getSingletonGson().fromJson(text, type)
        } catch (e: JsonSyntaxException) {
            // 返回结果读取异常
            throw DataException(mApplication.getString(R.string.http_data_explain_error), e)
        }

        if (result is HttpData<*>) {
            val model = result
            val headers = response.headers()
            val headersSize = headers.size()
            val headersMap: MutableMap<String, String> = HashMap(headersSize)
            for (i in 0 until headersSize) {
                headersMap[headers.name(i)] = headers.value(i)
            }
            // Github issue 地址：https://github.com/getActivity/EasyHttp/issues/233
            model.setResponseHeaders(headersMap)

            if (model.isRequestSuccess()) {
                // 代表执行成功
                return result
            }

            if (model.isTokenInvalidation()) {
                // 代表登录失效，需要重新登录
                throw TokenException(mApplication.getString(R.string.http_token_error))
            }

            // 代表执行失败
            throw ResultException(model.getMessage(), model)
        }
        return result
    }

    override fun requestFail(httpRequest: HttpRequest<*>, throwable: Throwable): Throwable {
        if (throwable is HttpException) {
            if (throwable is TokenException) {
                // 登录信息失效，跳转到登录页
            }
            return throwable
        }

        if (throwable is SocketTimeoutException) {
            return TimeoutException(
                mApplication.getString(R.string.http_server_out_time),
                throwable
            )
        }

        if (throwable is UnknownHostException) {
            val info =
                (mApplication.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager).activeNetworkInfo
            // 判断网络是否连接
            if (info != null && info.isConnected) {
                // 有连接就是服务器的问题
                return ServerException(
                    mApplication.getString(R.string.http_server_error),
                    throwable
                )
            }
            // 没有连接就是网络异常
            return NetworkException(mApplication.getString(R.string.http_network_error), throwable)
        }

        if (throwable is IOException) {
            // 出现该异常的两种情况
            // 1. 调用 EasyHttp 取消请求
            // 2. 网络请求被中断
            return CancelException(mApplication.getString(R.string.http_request_cancel), throwable)
        }

        return HttpException(throwable.message, throwable)
    }

    override fun downloadFail(httpRequest: HttpRequest<*>, throwable: Throwable): Throwable {
        if (throwable is ResponseException) {
            val responseException = throwable
            val response = responseException.response
            responseException.setMessage(
                String.format(
                    mApplication.getString(R.string.http_response_error),
                    response.code(), response.message()
                )
            )
            return responseException
        } else if (throwable is NullBodyException) {
            val nullBodyException = throwable
            nullBodyException.setMessage(mApplication.getString(R.string.http_response_null_body))
            return nullBodyException
        } else if (throwable is FileMd5Exception) {
            val fileMd5Exception = throwable
            fileMd5Exception.setMessage(mApplication.getString(R.string.http_response_md5_error))
            return fileMd5Exception
        }
        return requestFail(httpRequest, throwable)
    }

    override fun readCache(httpRequest: HttpRequest<*>, type: Type, cacheTime: Long): Any? {
        val cacheKey = generateCacheKey(httpRequest)
        val cacheValue = readHttpCache(cacheKey)
        if (cacheValue == null || "" == cacheValue || "{}" == cacheValue) {
            return null
        }
        EasyLog.printLog(httpRequest, "----- read cache key -----")
        EasyLog.printJson(httpRequest, cacheKey)
        EasyLog.printLog(httpRequest, "----- read cache value -----")
        EasyLog.printJson(httpRequest, cacheValue)
        EasyLog.printLog(httpRequest, "cacheTime = $cacheTime")
        val cacheInvalidate = isCacheInvalidate(cacheKey, cacheTime)
        EasyLog.printLog(httpRequest, "cacheInvalidate = $cacheInvalidate")
        if (cacheInvalidate) {
            // 表示缓存已经过期了，直接返回 null 给外层，表示缓存不可用
            return null
        }
        return GsonFactory.getSingletonGson().fromJson(cacheValue, type)
    }

    override fun writeCache(httpRequest: HttpRequest<*>, response: Response, result: Any): Boolean {
        val cacheKey = generateCacheKey(httpRequest)
        val cacheValue = GsonFactory.getSingletonGson().toJson(result)
        if (cacheValue == null || "" == cacheValue || "{}" == cacheValue) {
            return false
        }
        EasyLog.printLog(httpRequest, "----- write cache key -----")
        EasyLog.printJson(httpRequest, cacheKey)
        EasyLog.printLog(httpRequest, "----- write cache value -----")
        EasyLog.printJson(httpRequest, cacheValue)
        val writeHttpCacheResult = writeHttpCache(cacheKey, cacheValue)
        EasyLog.printLog(httpRequest, "writeHttpCacheResult = $writeHttpCacheResult")
        val refreshHttpCacheTimeResult = setHttpCacheTime(cacheKey, System.currentTimeMillis())
        EasyLog.printLog(httpRequest, "refreshHttpCacheTimeResult = $refreshHttpCacheTimeResult")
        return writeHttpCacheResult && refreshHttpCacheTimeResult
    }

    override fun clearCache() {
        HttpCacheManager.clearCache()
    }
}